#include <sstream>
#include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <nice2d/application.h>
#include <nice2d/piece/piece.h>
#include <nice2d/util.h>
#include <nice2d/components/fontmanager.h>
#include <nice2d/components/imagemanager.h>
#include <nice2d/components/done.h>
#include <nice2d/components/selectable.h>

IMPLEMENT_OBJECT(Piece, Sprite)

class PieceData
{
public:
    PieceData(Piece* newPiece):
        piece(newPiece)
    {
    }

    Piece* piece = nullptr;
    int32_t pieceType = 0;

    int32_t attributes[4] = {1};

    std::vector<sf::Sprite> sprites;
    sf::Text text;

    std::wstring name;
    std::wstring description;
    bool configurated = false;
    std::weak_ptr<Tile> configuratedTile;

    void updateDisplayString()
    {
        std::stringstream stream;
        stream << attributes[1]<<"-"<<attributes[2]<<"-"<<attributes[3];
        text.setString(stream.str());
    }

    void updateSpriteAndTextPosition()
    {
        auto box = piece->getTransform().transformRect(text.getLocalBounds());
        auto objectBox = piece->getBoundingBox();

        auto newPosition = piece->adjustPosition({objectBox.width,objectBox.height}, sf::Vector2f(box.width, box.height),HMode_Center,VMode_Bottom);
        newPosition.y -= box.top;
        newPosition.y += objectBox.top;
        text.setPosition(newPosition);

        if(sprites.empty())
            return;

        auto itr = sprites.begin();
        while(itr != sprites.end())
        {
            box = piece->getTransform().transformRect(itr->getGlobalBounds());

            newPosition = piece->adjustPosition({objectBox.width,objectBox.height}, sf::Vector2f(box.width, box.height),HMode_Center,VMode_Top);
            newPosition.y -= box.top;
            newPosition.y += objectBox.top;
            itr->setPosition(newPosition);
            itr ++;
        }
    }
};

Piece::Piece(const TextOption& option,const std::wstring &name):
    data(new PieceData(this))
{
    TextOption::apply(data->text,option);

    data->updateDisplayString();
    data->name = name;

    data->text.setPosition(12,56);

    ADD_COMPONENT(this,Done);
    ADD_COMPONENT(this,Selectable);
}

Piece::~Piece()
{
}

void Piece::setPieceType(int32_t type)
{
    data->pieceType = type;
}

int Piece::getPieceType() const
{
    return data->pieceType;
}

void Piece::setHitPoint(uint32_t value)
{
    data->attributes[0] = value;
}

uint32_t Piece::getHitPoint() const
{
    return data->attributes[0];
}

bool Piece::isDeath() const
{
    return getHitPoint() == 0;
}

void Piece::setAttack(uint32_t value)
{
    data->attributes[1] = value;
    data->updateDisplayString();
}

uint32_t Piece::getAttack() const
{
    return data->attributes[1];
}

void Piece::setDefense(uint32_t value)
{
    data->attributes[2] = value;
    data->updateDisplayString();
}

uint32_t Piece::getDefence() const
{
    return data->attributes[2];
}

void Piece::setMovement(uint32_t value)
{
    data->attributes[3] = value;
    data->updateDisplayString();
}

uint32_t Piece::getMovement() const
{
    return data->attributes[3];
}

void Piece::addPieceIcon(const std::string &file, const sf::IntRect &area)
{
    auto textureManager = GET_COMPONENT(Application::getInstance(), ImageManager);
    auto texture = textureManager->loadTextureFromFile(file);
    assert(texture);

    sf::Sprite sprite;
    sprite.setColor(getSpriteColor());
    sprite.setTexture(*texture);
    if (area.width != 0)
        sprite.setTextureRect(area);

    sprite.setScale(0.8f,0.8f);

    data->sprites.push_back(sprite);
}

void Piece::setPieceIcons(std::shared_ptr<ImageSet> set)
{
    if(set)
        set->visitImage([&](const std::string &file, const sf::IntRect &area){addPieceIcon(file,area);});
}

void Piece::setName(const std::wstring &name)
{
    data->name = name;
}

std::wstring Piece::getName() const
{
    return data->name;
}

void Piece::setDescription(const std::wstring &description)
{
    data->description = description;
}

std::wstring Piece::getDescription() const
{
    return data->description;
}

void Piece::configurate()
{
    data->configurated = true;
}

bool Piece::isConfigurated() const
{
    return data->configurated;
}

void Piece::setConfiguratedTile(std::shared_ptr<Tile> tile)
{
    data->configuratedTile = tile;
}

std::weak_ptr<Tile> Piece::getConfiguratedTile()
{
    return data->configuratedTile;
}

void Piece::setTextColor(const sf::Color &color)
{
    data->text.setFillColor(color);
}

sf::Color Piece::getTextColor() const
{
    return data->text.getFillColor();
}

PTree Piece::serialize()
{
    PTree node;
    SERIALIZE_BASE_OBJECT(node, Sprite)
    //node.add("name", data->name);
    //node.add("description", data->description);
    //TODO
    return node;
}

void Piece::deserialize(const PTree &node)
{
    //TODO
}

void Piece::onUpdateSprite(float deltaTime)
{
}

void Piece::onDrawSprite(sf::RenderTarget &target, sf::RenderStates states) const
{
    for(auto itr = data->sprites.begin();itr != data->sprites.end();itr++)
        target.draw(*itr,states);
    target.draw(data->text,states);
}

void Piece::onSpriteColorChanged()
{
    for(auto itr = data->sprites.begin();itr != data->sprites.end();itr++)
        (*itr).setColor(getSpriteColor());
}

void Piece::onPositionChanged()
{
    Sprite::onPositionChanged();
    data->updateSpriteAndTextPosition();
}

void Piece::onSizeChanged()
{
    Sprite::onSizeChanged();
    data->updateSpriteAndTextPosition();
}

void Piece::onRotateChanged()
{
    Sprite::onRotateChanged();
    data->updateSpriteAndTextPosition();
}

void Piece::onScaleChanged()
{
    Sprite::onScaleChanged();
    data->updateSpriteAndTextPosition();
}
